Skip to content

feat(dashboard): add read-only fleet status HTTP dashboard#410

Merged
gfargo-horizon-agent[bot] merged 2 commits into
mainfrom
agent/strut-751-strut-335-p3-feat-lightweight-status-das
Jul 12, 2026
Merged

feat(dashboard): add read-only fleet status HTTP dashboard#410
gfargo-horizon-agent[bot] merged 2 commits into
mainfrom
agent/strut-751-strut-335-p3-feat-lightweight-status-das

Conversation

@gfargo-horizon-agent

@gfargo-horizon-agent gfargo-horizon-agent Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

What

Adds a strut dashboard command that starts a zero-dependency (socat) HTTP server exposing fleet + stack health as an auto-refreshing HTML page and JSON API endpoints — answering "is my fleet healthy right now?" without SSHing into each box.

Why

Closes #335
Plane: OSS-751

How

  • lib/cmd_dashboard.sh: cmd_dashboard (arg parsing, socat guard, cache-dir setup) + a per-connection heredoc handler (mirrors lib/cmd_webhook.sh's serve pattern) routing /, /api/fleet, /api/stacks, /api/drift
  • /api/fleetstrut fleet status --json; /api/stacksstrut status-all --json; /api/drift → new per-stack aggregation (no existing fleet-wide drift aggregator)
  • Each endpoint's output is file-cached for 30s (socat's fork spawns one process per connection, so in-memory caching isn't possible)
  • _dashboard_render_html renders the HTML table (host status/behind/dirty + per-stack health), degrading to a <pre> JSON dump when jq is unavailable
  • Binds 127.0.0.1 by default (--bind to override); --json makes GET / return JSON instead of HTML
  • Wired into the strut entrypoint (lazy-sourced, like status-all), shell completions, and README

Testing

  • bats tests/test_cmd_dashboard.bats — 19/19 passing (arg parsing, socat guard, cache TTL behavior, drift aggregation, HTML rendering incl. HTML-escaping)
  • bats tests/ — full suite passes; the ~55 pre-existing failures (audit/backup/keys/rollback JSON tests) reproduce identically on main in this sandbox (missing jq) and are unrelated to this change
  • lint clean (shellcheck unavailable in this sandbox — please verify in CI)
  • CI: pending

Closes #335

Adds `strut dashboard [--port N] [--bind addr] [--json]`, a zero-dependency
(socat) HTTP server exposing fleet + stack health as an auto-refreshing
HTML page or JSON, so operators can check "is my fleet healthy" without
SSHing into each host. Binds to 127.0.0.1 by default. Reuses `fleet status
--json` and `status-all --json`, with per-stack drift aggregated fresh at
/api/drift, and caches each 30s so rapid refreshes don't re-run SSH probes.

@gfargo-horizon-agent gfargo-horizon-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔎 Agent review (sonnet→opus) — CONCERNS

REVIEW: CONCERNS
RESOLVES: full

The command faithfully implements the plan (socat handler, 30s file cache, /api/fleet|stacks|drift, HTML render with jq degradation, localhost bind, good unit tests) and meets the JSON/HTML acceptance criteria. Two real concerns remain: the temp cache dir leaks on Ctrl+C, and every HTTP response depends on bash-only source/pipefail that socat's /bin/sh handler may not support.

2 concerns · 1 nit — 3 inline on the diff

Comment thread lib/cmd_dashboard.sh
Comment thread lib/cmd_dashboard.sh
Comment thread lib/cmd_dashboard.sh

@gfargo gfargo left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Address the concerns and NITs please

Addresses PR review feedback on #410:
- Register INT/TERM traps that remove the cache dir before exiting,
  since socat blocks in the foreground and the EXIT-only cleanup
  chain never fires on Ctrl+C.
- Run the per-connection handler via socat's EXEC (fork+exec) instead
  of SYSTEM (fork+`/bin/sh -c`), writing it to an executable file with
  a bash shebang so it isn't at the mercy of the host's /bin/sh (dash
  on Debian/Ubuntu silently breaks the handler's source/pipefail use).
- Show actual cache freshness ("12s ago") in the HTML "Last refresh"
  line instead of the page-render timestamp, and document why the
  host table uses a Branch column rather than the mock's Last Deploy
  (fleet status has no per-host deploy timestamp).

@gfargo-horizon-agent gfargo-horizon-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔎 Agent re-review (sonnet, delta) — LGTM

REVIEW: LGTM
RESOLVES: full

The revise correctly fixes both prior issues (SIGINT/SIGTERM cache cleanup via INT/TERM traps mirroring the existing cmd_group.sh pattern, and EXEC+bash-shebang handler to avoid dash /bin/sh) and adds matching test coverage; no regressions or scope creep found.

1 nit — 1 inline on the diff

@test "_dashboard_cache_age: reports seconds since the cache file was written" {
export _DASH_CACHE_DIR="$BATS_TEST_TMPDIR"
echo '{}' > "$BATS_TEST_TMPDIR/agekey.json"
touch -d "@$(($(date +%s) - 15))" "$BATS_TEST_TMPDIR/agekey.json"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 New test uses GNU-only touch -d

The new _dashboard_cache_age test sets an mtime with touch -d "@$(...)", which is GNU-coreutils-only syntax. Every other mtime-manipulating test in this suite (test_backup_helpers.bats, test_backup_retention.bats, test_status_all.bats) uses a date -v/date -d fallback chain for BSD/macOS portability, and _dashboard_file_mtime itself supports both stat -c and stat -f. This one test would fail on a macOS test runner.

@gfargo-horizon-agent gfargo-horizon-agent Bot merged commit 20d358b into main Jul 12, 2026
3 checks passed
@gfargo-horizon-agent gfargo-horizon-agent Bot deleted the agent/strut-751-strut-335-p3-feat-lightweight-status-das branch July 12, 2026 18:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P3] feat: lightweight status dashboard (read-only fleet health page)

1 participant